#include "StdAfx.h"

MainMenu* Singleton<MainMenu>::m_instance = 0;

unsigned long __stdcall _ADT_LOAD_PROC(LPVOID)
{
	/*while(sD3D.GetAppState() == STATE_LOADING)
	{
		sD3D.Render();
	}
	Log::Print("Finished...\n");*/
	ExitThread(0);
}

MainMenu::MainMenu() :
	m_miniMap(0),
	m_curMiniMap(NULL),
	mEntryFont("Fonts\\FrizQT__.ttf", "Friz Quadrata TT", 8, 16)
{
	loadTexture();
	mLastState = sD3D.GetAppState();
	float totSizeX = sD3D.GetSize().x - 40;
	ui32 nButtons = totSizeX / 250;
	for(ui32 i = 0, j = 0; i < dbcMap.GetRowCount(); ++i)
	{
		auto mapEntry = dbcMap.GetRow(i);
		std::stringstream wdl;
		wdl << "World\\Maps\\" << mapEntry->mapName << "\\" << mapEntry->mapName << ".wdl";
		CMpqFile wdlFile(wdl.str());
		if(!wdlFile.IsOpened())
			continue;

		ui32 posX = j % nButtons;
		ui32 posZ = j / nButtons;
		MainMenuEntry entry;
		entry.color = 0xFFFFFFFF;
		std::vector<wchar_t> unicode(strlen(mapEntry->displayName) + 1);
		MultiByteToWideChar(CP_UTF8, 0, mapEntry->displayName, unicode.size(), &unicode[0], unicode.size());
		entry.wszText = &unicode[0];
		entry.id = mapEntry->id;
		mEntries.push_back(entry);

		++j;
	}

	std::sort(mEntries.begin(), mEntries.end());

	for(ui32 i = 0; i < mEntries.size(); ++i) {
		ui32 posX = i % nButtons;
		ui32 posZ = i / nButtons;

		mEntries[i].x = posX * 250 + 20;
		mEntries[i].y = posZ * 20 + 30;

		mSortedEntries[mEntries[i].wszText[0]].push_back(mEntries[i]);
	}
	
	m_back = new UIButton<MainMenu>(this, Vertex2F(650, 500), Vertex2F(100, 30), "Back", 1);
	m_back->SetOnClick(&MainMenu::OnButtonPressed);
	m_adtHover = new UILabel(Vertex2F(650, 200), Vertex2F(300, 30), "Hovered ADT: ");
	m_minMapHelp1 = new UILabel(Vertex2F(10, 630), Vertex2F(1000, 30), "Use the mousewheel to zoom in and out of the minimap");
	m_minMapHelp2 = new UILabel(Vertex2F(10, 655), Vertex2F(1000, 30), "Press the right mouse button and move the mouse around to drag the minimap!");
	sUI.AddElement(m_adtHover);
	sUI.AddElement(m_back);
	sUI.AddElement(m_minMapHelp1);
	sUI.AddElement(m_minMapHelp2);
	m_back->pData = (void*)0xFFFFFFFF;
	m_scaleFactor = 1.0f;
	m_relFactor.x = m_relFactor.y = 0.0f;
	m_mouseLast.x = m_mouseLast.y = -1.0f;

	m_locScroll = new VScrollbar<MainMenu>(sD3D.GetSize().x - 30, 20, sD3D.GetSize().y - 40, this);
	m_locScroll->SetRange(0, 100);
	m_locScroll->SetValue(0);
	sUI.AddElement(m_locScroll);
}

void MainMenu::BeforeReset()
{
	mBatch.onDeviceLost();
}

void MainMenu::OnReset()
{
	mBatch.onDeviceReset();
}

void MainMenu::Render()
{
	if(sD3D.GetAppState() == STATE_MENU) {
		_Vertex3FRHWT backg[4] =
		{
			{ 0, 0, 0, 1, 0, 0 },
			{ sD3D.GetSize().x, 0, 0, 1, 1, 0 },
			{ sD3D.GetSize().x, sD3D.GetSize().y, 0, 1, 1, 1 },
			{ 0, sD3D.GetSize().y, 0, 1, 0, 1 }
		};

		sD3D->SetTexture(0, mBackgTexture);
		sD3D->SetFVF(Vertex3FRHWT::FVF);
		sD3D->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, backg, sizeof(_Vertex3FRHWT));
		m_locScroll->m_visible = true;
	} else {
		m_locScroll->m_visible = false;
	}

	if(sD3D.GetAppState() != STATE_MENU)
	{
	}
	else
	{
		ui32 fullSize = calcMenuSize();
		if(sD3D.GetSize().y - 10 < fullSize) {
			ui32 range = fullSize - sD3D.GetSize().y + 10;
			m_locScroll->SetRange(0, range);
			m_locScroll->setPosition(sD3D.GetSize().x - 30, 20);
			m_locScroll->setHeight(sD3D.GetSize().y - 40);
			m_locScroll->m_visible = true;
			m_locScroll->Draw();
		} else {
			m_locScroll->m_visible = false;
			m_locScroll->SetValue(0);
		}

		mBatch.beginBatch();

		ui32 curHeight = 10;
		float totSizeX = sD3D.GetSize().x - 40;
		ui32 nButtons = totSizeX / 280;
		std::for_each(mSortedEntries.begin(), mSortedEntries.end(),
			[this, &curHeight, nButtons](std::map<wchar_t, std::vector<MainMenuEntry>>::value_type& val) {
				ui32 curIndex = 0;
				std::wstring text;
				text += val.first;
				mBatch.drawTextW(mEntryFont, 20, curHeight - m_locScroll->GetValue(), 30, 30, text, 0xFF0033CC);
				curHeight += 15;
				
				ui32 heightTmp = curHeight;
				MainMenu* men = this;
				ui32 buttons = nButtons;
				std::for_each(val.second.begin(), val.second.end(),
					[men, &heightTmp, buttons, &curIndex](MainMenuEntry& e) {
						ui32 posX = (curIndex % buttons) * 280 + 20;
						ui32 posY = heightTmp + (curIndex / buttons) * 15;
						men->mBatch.drawTextW(men->mEntryFont, posX, posY - men->m_locScroll->GetValue(), 520, 30, e.wszText, e.color);
						++curIndex;
				}
				);
				curHeight += (val.second.size() / nButtons) * 15 + 20;
		}
		);
		mBatch.endBatch();
	}
	if(sD3D.GetAppState() != STATE_MINIMAP) {
		if(m_curMiniMap != NULL) {
			m_curMiniMap->unload();
			delete m_curMiniMap;
			m_curMiniMap = NULL;
		}
	}
	if(sD3D.GetAppState() == STATE_MINIMAP)
	{
		if(m_curMiniMap != NULL)
			m_curMiniMap->Draw();
		m_back->m_visible = true;
		m_adtHover->m_visible = true;
		m_minMapHelp1->m_visible = true;
		m_minMapHelp2->m_visible = true;
	}
	else
	{
		m_back->m_visible = false;
		m_minMapHelp1->m_visible = false;
		m_minMapHelp2->m_visible = false;
		m_adtHover->m_visible = false;
	}

	mLastState = sD3D.GetAppState();
}

void MainMenu::OnMouseMove(Vertex2F pos, bool rButton)
{
	if(sD3D.GetAppState() == STATE_MENU) {
		colorMenuEntries(pos.x, pos.y);
	}
	if(sD3D.GetAppState() != STATE_MINIMAP)
		return;
	if(rButton)
	{
		if(m_mouseLast.x == -1 && m_mouseLast.y == -1)
		{
			m_mouseLast.x = pos.x;
			m_mouseLast.y = pos.y;
			return;
		}
		m_relFactor.x -= (pos.x - m_mouseLast.x) * (0.001f * m_scaleFactor);
		m_relFactor.x = max(min(m_relFactor.x, (0.5f - m_scaleFactor / 2)), -(0.5f - m_scaleFactor / 2));
		m_relFactor.y -= (pos.y - m_mouseLast.y) * (0.001f * m_scaleFactor);
		m_relFactor.y = max(min(m_relFactor.y, (0.5f - m_scaleFactor / 2)), -(0.5f - m_scaleFactor / 2));
		if(m_curMiniMap != NULL)
			m_curMiniMap->SetRelation(m_relFactor);
		m_mouseLast = pos;
		return;
	}

	m_mouseLast = pos;

	if(pos.x >= 100 && pos.x <= 612 && pos.y >= 100 && pos.y <= 612)
	{
		float numAdts = 64 * m_scaleFactor;
		float factor = 512.0f / numAdts;
		float totCoord = numAdts * TILESIZE;
		float diffX = (64.0f * TILESIZE);
		float diffY = (64.0f * TILESIZE);
		float ofsX = (0.5f - m_scaleFactor / 2) + m_relFactor.x;
		float ofsY = (0.5f - m_scaleFactor / 2) + m_relFactor.y;
		diffX *= ofsX;
		diffY *= ofsY;
		float coordspp = totCoord / 512.0f;
		float coordX = (pos.x - 100) * coordspp + diffX;
		float coordY = (pos.y - 100) * coordspp + diffY;
		ui32 indexX = (ui32)floor(coordX / TILESIZE);
		ui32 indexZ = (ui32)floor(coordY / TILESIZE);
		char msg[255];
		sprintf_s<255>(msg, "Hovered ADT: %u/%u", indexX, indexZ);
		m_adtHover->SetText(msg);
	}
	else
	{
		m_adtHover->SetText("Hovered ADT: (none)");
	}
}

void MainMenu::OnButtonPressed(UIButton<MainMenu>* pButton)
{
	if((ui32)pButton->pData == 0xFFFFFFFF)
	{
		sD3D.SetAppState(STATE_MENU);
		m_miniMap = 0;
		return;
	}
	m_miniMap = (ui32)pButton->pData;
	m_curMiniMap = new MiniMap(dbcMap.LookupEntry(m_miniMap)->mapName, Vertex2F(100, 100), Vertex2F(512, 512));
	m_curMiniMap->InitTexture(dbcMap.LookupEntry(m_miniMap)->mapName);
	m_scaleFactor = 1.0f;
	m_relFactor.x = m_relFactor.y = 0.0f;
	sD3D.SetAppState(STATE_MINIMAP);
}

bool MainMenu::OnMouseClick(Vertex2F pos)
{
	if(sD3D.GetAppState() == STATE_MENU) {
		auto entry = getEntryForPos(pos.x, pos.y);
		if(entry == NULL)
			return false;

		m_miniMap = entry->id;
		m_curMiniMap = new MiniMap(dbcMap.LookupEntry(m_miniMap)->mapName, Vertex2F(100, 100), Vertex2F(512, 512));
		m_curMiniMap->InitTexture(dbcMap.LookupEntry(m_miniMap)->mapName);
		m_scaleFactor = 1.0f;
		m_relFactor.x = m_relFactor.y = 0.0f;
		sD3D.SetAppState(STATE_MINIMAP);
		return false;
	}
	if(sD3D.GetAppState() != STATE_MINIMAP)
		return false;
	if(pos.x >= 100 && pos.x <= 612 && pos.y >= 100 && pos.y <= 612)
	{
		float numAdts = 64 * m_scaleFactor;
		float factor = 512.0f / numAdts;
		float totCoord = numAdts * TILESIZE;
		float diffX = (64.0f * TILESIZE);
		float diffY = (64.0f * TILESIZE);
		float ofsX = (0.5f - m_scaleFactor / 2) + m_relFactor.x;
		float ofsY = (0.5f - m_scaleFactor / 2) + m_relFactor.y;
		diffX *= ofsX;
		diffY *= ofsY;
		float coordspp = totCoord / 512.0f;
		float coordX = (pos.x - 100) * coordspp + diffX;
		float coordY = (pos.y - 100) * coordspp + diffY;
		ui32 indexX = (ui32)floor(coordX / TILESIZE);
		ui32 indexZ = (ui32)floor(coordY / TILESIZE);
		if(dbcMap.LookupEntry(m_miniMap) != NULL)
		{
			sLoading.SetMap(m_miniMap);
			sD3D.SetAppState(STATE_LOADING);
			Loader::GetInstance()->SetMapId(m_miniMap);
			bool found = false;
			for(i32 i = -1; i < 2; ++i)
			{
				for(i32 j = -1; j < 2; ++j)
				{
					MapEntry* me = dbcMap.LookupEntry(m_miniMap);
					std::stringstream name;
					name << "World\\Maps\\" << me->mapName << "\\" << me->mapName << "_" << indexX + i << "_" << indexZ + j << ".adt";
					Loader::GetInstance()->SetAdt(i, j, ((indexX + i) * 100) + (indexZ + j));
					ADTModel* model = new ADTModel(name.str());
					model->m_indexX = indexX + i;
					model->m_indexZ = indexZ + j;
					if(model->Init(true))
					{
						sD3D.AddAdt(model, indexX + i, indexZ + j);
						found = true;
					}
				}
			}
			if(!found)
			{
				sD3D.SetAppState(STATE_MINIMAP);
				sD3D.Render();
				return true;
			}

			std::string continent = dbcMap.LookupEntry(m_miniMap)->mapName;
			float x = (indexX + 0.51f) * TILESIZE;
			float z = (indexZ + 0.51f) * TILESIZE;
			float y = 0.0f;
			sD3D.getLandHeightFast(x, z, y);
			y += 100.0f;		
			sCamera.SetCameraPosition(x, y, z);
			sSelection.SetEntryPoint(Vertex3F(x, y, z), m_miniMap); 
			sTerrainMgr.resetToAdt(indexX, indexZ, continent);
			sMouse.ResetDownTime(true);
			sD3D.SetAppState(STATE_WORLD);
			sD3D.Render();
			sCommUI.Toggle();
			return true;
		}
	}
	return false;
}

void MainMenu::MouseWheel(i16 up)
{
	if(sD3D.GetAppState() == STATE_MENU) {
		if(m_locScroll->m_visible == true) {
			if(up < 0)
				m_locScroll->increment();
			else
				m_locScroll->decrement();
		}
	}
	if(sD3D.GetAppState() == STATE_MINIMAP) {
		if(up < 0)
			m_scaleFactor -= 0.1f;
		else
			m_scaleFactor += 0.1f;
		m_scaleFactor = min(max(m_scaleFactor, 0.1f), 1.0f);
		m_relFactor.x = max(min(m_relFactor.x, 0.5f - m_scaleFactor / 2), -(0.5f - m_scaleFactor / 2));
		m_relFactor.y = max(min(m_relFactor.y, 0.5f - m_scaleFactor / 2), -(0.5f - m_scaleFactor / 2));

		if(m_curMiniMap != NULL) {
			m_curMiniMap->SetScale(m_scaleFactor);
			m_curMiniMap->SetRelation(m_relFactor);
		}
	}
}

void MainMenu::loadTexture() {
	HINSTANCE hInst = GetModuleHandle(NULL);
	HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(IDR_BINDATA1), "BINDATA");
	DWORD dwSize = SizeofResource(hInst, hRes);
	HGLOBAL hGlob = LoadResource(hInst, hRes);
	LPBYTE pBytes = (LPBYTE)LockResource(hGlob);

	D3DXCreateTextureFromFileInMemory(sD3D, pBytes, dwSize, &mBackgTexture);
}

ui32 MainMenu::calcMenuSize() {
	ui32 curHeight = 10;
	float totSizeX = sD3D.GetSize().x - 40;
	ui32 nButtons = totSizeX / 280;
	std::for_each(mSortedEntries.begin(), mSortedEntries.end(),
		[this, &curHeight, nButtons](std::map<wchar_t, std::vector<MainMenuEntry>>::value_type& val) {
			ui32 curIndex = 0;
			curHeight += 15;

			ui32 heightTmp = curHeight;
			ui32 buttons = nButtons;
			std::for_each(val.second.begin(), val.second.end(),
				[&heightTmp, buttons, &curIndex](MainMenuEntry& e) {
					ui32 posY = heightTmp + (curIndex / buttons) * 15;
					++curIndex;
			}
			);
			curHeight += (val.second.size() / nButtons) * 15 + 20;
	}
	);

	return curHeight;
}

MainMenuEntry* MainMenu::getEntryForPos(ui32 x, ui32 y) {
	ui32 curHeight = 10;
	float totSizeX = sD3D.GetSize().x - 40;
	ui32 nButtons = totSizeX / 280;
	MainMenuEntry* resEntry = NULL;
	std::for_each(mSortedEntries.begin(), mSortedEntries.end(),
		[this, &curHeight, nButtons, &resEntry, x, y](std::map<wchar_t, std::vector<MainMenuEntry>>::value_type& val) {
			ui32 curIndex = 0;
			curHeight += 15;

			ui32 heightTmp = curHeight;
			ui32 buttons = nButtons;
			MainMenuEntry* hitEntry = NULL;
			ui32 nx = x, ny = y;
			MainMenu* men = this;
			std::for_each(val.second.begin(), val.second.end(),
				[&heightTmp, buttons, &curIndex, &hitEntry, nx, ny, men](MainMenuEntry& e) {
					ui32 posX = (curIndex % buttons) * 280 + 20;
					ui32 posY = heightTmp + (curIndex / buttons) * 15 - men->m_locScroll->GetValue();
					if(nx >= posX && nx <= posX + 250 && ny >= posY && ny <= posY + 15) {
						hitEntry = &e;
					}
					++curIndex;
			}
			);
			curHeight += (val.second.size() / nButtons) * 15 + 20;
			if(hitEntry != NULL)
				resEntry = hitEntry;
	}
	);

	return resEntry;
}

void MainMenu::colorMenuEntries(ui32 x, ui32 y) {
	ui32 curHeight = 10;
	float totSizeX = sD3D.GetSize().x - 40;
	ui32 nButtons = totSizeX / 280;
	std::for_each(mSortedEntries.begin(), mSortedEntries.end(),
		[this, &curHeight, nButtons, x, y](std::map<wchar_t, std::vector<MainMenuEntry>>::value_type& val) {
			ui32 curIndex = 0;
			curHeight += 15;

			ui32 heightTmp = curHeight;
			ui32 buttons = nButtons;
			ui32 nx = x, ny = y;
			MainMenu* men = this;
			std::for_each(val.second.begin(), val.second.end(),
				[&heightTmp, buttons, &curIndex, nx, ny, men](MainMenuEntry& e) {
					ui32 posX = (curIndex % buttons) * 280 + 20;
					ui32 posY = heightTmp + (curIndex / buttons) * 15 - men->m_locScroll->GetValue();
					if(nx >= posX && nx <= posX + 250 && ny >= posY && ny <= posY + 15) {
						e.color = 0xFFFFFFFF;
					} else {
						e.color = 0xFFFF3320;
					}
					++curIndex;
			}
			);
			curHeight += (val.second.size() / nButtons) * 15 + 20;
	}
	);
}